/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package wwcontrolserver.ifk;

import wwcontrolserver.datarecorders.AbstractDataRecorder;
import wwcontrolserver.datarecorders.WWControlServerLogger;
import wwcontrolserver.sensor.SensorRKey;
import wwcontrolserver.sensor.SensorKey;
import wwcontrolserver.sensor.AbstractSensor;
import com.phidgets.PhidgetException;
import com.phidgets.event.AttachListener;
import com.phidgets.event.DetachListener;
import com.phidgets.event.InputChangeListener;
import com.phidgets.event.OutputChangeListener;
import com.phidgets.event.SensorChangeListener;
import java.util.ArrayList;
import java.util.HashMap;
import wwcontrolserver.sensor.SensorContainer;
//import wwcontrolserver.AbstractSensor.SensorRKey;

/**
 *
 * @author Michael Ferry
 */
public class AnalogInputIFK extends AbstractIFK {
    private static final long DEFAULT_RATIOMETRIC_PAUSETIME = 500;
    //private final WWDatabaseConnection WWDB;
    private final boolean DefaultRatioMetric;
    //private final boolean MixedRatioMetricSensorsAttached;
    //public static final double TENBIT_DYNR = 1.0;
    //public static final double TWELVEBIT_DYNR = 4.096;
    private final DYNR SamplingRange;
    private final double MAX_SVAL;
    //private final ArrayList<AbstractSensor> RatiometricSensors;
    //private final ArrayList<AbstractSensor> NonRatiometricSensors;
    private final ArrayList<AbstractSensor> PrimaryModeSensors;
    private final ArrayList<AbstractSensor> SecondaryModeSensors;
    private final HashMap<Integer,AbstractSensor> AttachedSensorMap;
    //private final HashMap<Integer,AbstractSensor> AttachedSensors;

    public AnalogInputIFK(int ifkSerial, String ifkName, SensorContainer SContainer,
            AttachListener AL, DetachListener DL, DYNR SamplingRange) throws PhidgetException {

        super(ifkSerial, ifkName, AL, DL);
        //this.DefaultRatioMetric = DefaultRatioMetric;
        this.SamplingRange = SamplingRange;
        this.MAX_SVAL = this.SamplingRange.GetMax();

        //SetupSensorArrayLists();
        ArrayList<AbstractSensor> RatiometricSensors = SContainer.GetSensorsMatchingSRKey(new SensorRKey(ifkSerial, true));
        ArrayList<AbstractSensor> NonRatiometricSensors = SContainer.GetSensorsMatchingSRKey(new SensorRKey(ifkSerial, false));
        AttachedSensorMap = SContainer.GetHashMapOfSensorsAttachedToIFK(ifkSerial);
        //DefaultRatioMetric = DetermineAndSetRatiometricState();
        //MixedRatioMetricSensorsAttached = DetermineMultipleRatiometric();

        //Determine the ratiometric state of the ifk
        if (RatiometricSensors == null && NonRatiometricSensors == null) {
            DefaultRatioMetric = true;
            PrimaryModeSensors = null;
            SecondaryModeSensors = null;
        } else if (RatiometricSensors == null && NonRatiometricSensors != null) {
            DefaultRatioMetric = false;
            PrimaryModeSensors = NonRatiometricSensors;
            SecondaryModeSensors = null;
        } else if (RatiometricSensors != null && NonRatiometricSensors == null) {
            DefaultRatioMetric = true;
            PrimaryModeSensors = RatiometricSensors;
            SecondaryModeSensors = null;
        } else if (RatiometricSensors.size() > NonRatiometricSensors.size()) {
            DefaultRatioMetric = true;
            PrimaryModeSensors = RatiometricSensors;
            SecondaryModeSensors = NonRatiometricSensors;
        } else {
            DefaultRatioMetric = false;
            PrimaryModeSensors = NonRatiometricSensors;
            SecondaryModeSensors = RatiometricSensors;
        }

    }
    //This function should be called upon the attachement of the Phidget in the default attachment portion of the code
    public void SetRatiometric(boolean ratiometric) throws PhidgetException {
        ifk.setRatiometric(ratiometric);
        
    }
    //Use this to give the A/D conversion chip time to switch ratiometric states
    public void SetRatiometric(boolean ratiometric, long SleepTime) throws PhidgetException, InterruptedException{
        ifk.setRatiometric(ratiometric);
        Thread.sleep(SleepTime);
    }
    public boolean GetRatiometric() throws PhidgetException {
        return ifk.getRatiometric();
    }
//    public int GetSensorValue(int SNUM) throws PhidgetException{
//        return ifk.getSensorValue(SNUM);
//    }
//    //This uses high DYNR
//    public int GetSensorRawValue(int SNUM) throws PhidgetException {
//        return ifk.getSensorRawValue(SNUM);
        
 //   }
    public int GetSVAL(int SNUM, DYNR SRange) throws PhidgetException {
        //if (ifk.isAttached()) {
            switch(SRange){
                case TENBIT:
                    return ifk.getSensorValue(SNUM);                   
                case TWELVEBIT:
                    return ifk.getSensorRawValue(SNUM);
            }
            //Default return value
            return ifk.getSensorRawValue(SNUM);
//            if (DYNR == TENBIT_DYNR) {
//                return ifk.getSensorValue(SNUM);
//            } else if (DYNR == TWELVEBIT_DYNR) {
//                return ifk.getSensorRawValue(SNUM);
//            } else {
//                //this would indicate an error, the DYNR was an unknown value
//                return -1;
//            }
       // } else {
       //     throw new PhidgetException(5, "Not Attached");
      //  }

    }
    public int GetSVAL(int SNUM) throws PhidgetException{
        return GetSVAL(SNUM,SamplingRange);
    }
    
    public double GetSensorComputedValue(int SNUM) throws PhidgetException {

        //AbstractSensor SensorToCompute;
        int SVAL = GetSVAL(SNUM);
        AbstractSensor SensorToCompute = AttachedSensorMap.get(SNUM);
        if (SensorToCompute != null) {
            return SensorToCompute.ComputedSensorValue(SVAL,MAX_SVAL);
        } else {
            return SVAL;
        }

    }
    public int GetSensorChangeTrigger(int SNUM) throws PhidgetException{
        return ifk.getSensorChangeTrigger(SNUM);
    }
    
    @Override
    public void RecordIFKState(WWControlServerLogger WWCSLog, ArrayList<AbstractDataRecorder> DataRecList) throws PhidgetException {
        //determine if ifk is currently attached
        if (ifk.isAttached()) {
            //Set the ratiometric state back to default if it is not already there          
            if(GetRatiometric() != DefaultRatioMetric){
                try {
                    SetRatiometric(DefaultRatioMetric, DEFAULT_RATIOMETRIC_PAUSETIME);
                } catch (InterruptedException e) {
                    WWCSLog.RecordExceptionLogEntry(e);
                }
            }
            if (PrimaryModeSensors != null) {
                RecordSensorArrayListValues(PrimaryModeSensors, DefaultRatioMetric, DataRecList);
                //SecondaryModeSensors will be null if there are only one ratiometric type of sensor attached
            }
            if (SecondaryModeSensors != null) {
                //Record sensor values with a pause to switch from default to opposite
                //ratiometric and back again
                try {
                    SetRatiometric(!DefaultRatioMetric, DEFAULT_RATIOMETRIC_PAUSETIME);
                } catch (InterruptedException e) {
                    WWCSLog.RecordExceptionLogEntry(e);
                }
                RecordSensorArrayListValues(SecondaryModeSensors, !DefaultRatioMetric, DataRecList);
                SetRatiometric(DefaultRatioMetric);
            }
            //insert the non attached record if sensor is not attached
        } else {
            for (AbstractDataRecorder DataRec : DataRecList) {
                DataRec.InsertSensorRecordNA(ifkSerial);
            }
        }
    }
    
    private void RecordSensorArrayListValues(ArrayList<AbstractSensor> SensorList,
            boolean RatiometricState, ArrayList<AbstractDataRecorder> DataRecList) throws PhidgetException {
        int RatiometricInt = RatiometricState ? 1 : 0;

        for (AbstractSensor s : SensorList) {
            int SNUM = s.GetSNUM();
            int SVAL = GetSVAL(SNUM);
            double CVAL = s.ComputedSensorValue(SVAL, MAX_SVAL);
            for (AbstractDataRecorder DataRec : DataRecList) {
                DataRec.InsertSensorRecord(ifkSerial, SNUM, SVAL, CVAL,
                        RatiometricInt, GetSensorChangeTrigger(SNUM), MAX_SVAL, 1);
            }
        }
    }
    
    @Override
    public  void AddOutputChangeListener(OutputChangeListener OL){
        ifk.addOutputChangeListener(OL);
    }
    @Override
    public void AddInputChangeListener(InputChangeListener IL){
        ifk.addInputChangeListener(IL);
    }
    @Override
    public void AddSensorChangeListener(SensorChangeListener SL){
        ifk.addSensorChangeListener(SL);
    }
    

    
    @Override
    //This code is executed upon 
    public void ExecuteDefaultAttach() throws PhidgetException{
        this.SetRatiometric(DefaultRatioMetric);
    }
    @Override
    public void ExecuteDefaultDetach(){
    }
}
